"""Redis工具类"""
import json
import logging
from typing import Optional, List, Any, Set
from app.config.redis import get_redis

logger = logging.getLogger(__name__)


class RedisUtil:
    """Redis操作工具类"""
    
    def __init__(self):
        self.redis_client = get_redis()
    
    def test_connection(self) -> bool:
        """测试Redis连接"""
        if self.redis_client is None:
            return False
        try:
            self.redis_client.ping()
            return True
        except Exception as e:
            logger.error(f"Redis连接测试失败: {e}")
            return False
    
    def set_cache(self, key: str, value: Any, expire: Optional[int] = None) -> bool:
        """设置缓存"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，跳过缓存设置: {key}")
            return False
        try:
            if isinstance(value, (dict, list)):
                value = json.dumps(value)
            result = self.redis_client.set(key, value)
            if expire:
                self.redis_client.expire(key, expire)
            return result
        except Exception as e:
            logger.error(f"设置缓存失败: {key}, {e}")
            return False
    
    def get_cache(self, key: str) -> Optional[Any]:
        """获取缓存"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法获取缓存: {key}")
            return None
        try:
            value = self.redis_client.get(key)
            if value:
                try:
                    return json.loads(value)
                except json.JSONDecodeError:
                    return value
            return None
        except Exception as e:
            logger.error(f"获取缓存失败: {key}, {e}")
            return None
    
    def delete_cache(self, key: str) -> bool:
        """删除缓存"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法删除缓存: {key}")
            return False
        try:
            return bool(self.redis_client.delete(key))
        except Exception as e:
            logger.error(f"删除缓存失败: {key}, {e}")
            return False
    
    def delete_pattern(self, pattern: str) -> int:
        """删除匹配模式的所有缓存"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法删除模式缓存: {pattern}")
            return 0
        try:
            keys = self.redis_client.keys(pattern)
            if keys:
                return self.redis_client.delete(*keys)
            return 0
        except Exception as e:
            logger.error(f"删除模式缓存失败: {pattern}, {e}")
            return 0
    
    # ZSET相关操作
    def zadd(self, key: str, score: float, value: Any) -> bool:
        """添加ZSET元素"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法添加ZSET元素: {key}")
            return False
        try:
            if isinstance(value, (dict, list)):
                value = json.dumps(value)
            return bool(self.redis_client.zadd(key, {value: score}))
        except Exception as e:
            logger.error(f"添加ZSET元素失败: {key}, {e}")
            return False
    
    def zrange(self, key: str, start: int, end: int, withscores: bool = False) -> List[Any]:
        """获取ZSET范围元素"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法获取ZSET元素: {key}")
            return []
        try:
            result = self.redis_client.zrange(key, start, end, withscores=withscores)
            if withscores:
                return [(json.loads(item[0]) if isinstance(item[0], str) and item[0].startswith('{') else item[0], item[1]) for item in result]
            else:
                return [json.loads(item) if isinstance(item, str) and item.startswith('{') else item for item in result]
        except Exception as e:
            logger.error(f"获取ZSET元素失败: {key}, {e}")
            return []
    
    def zcard(self, key: str) -> int:
        """获取ZSET元素数量"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法获取ZSET数量: {key}")
            return 0
        try:
            return self.redis_client.zcard(key)
        except Exception as e:
            logger.error(f"获取ZSET长度失败: {key}, {e}")
            return 0
    
    # 观看次数队列相关
    def add_view_count(self, video_id: int) -> bool:
        """添加观看次数到队列"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法添加观看次数: {video_id}")
            return False
        try:
            # 使用集合存储待更新的视频ID
            queue_key = "video:view_count:queue"
            return bool(self.redis_client.sadd(queue_key, video_id))
        except Exception as e:
            logger.error(f"添加观看次数失败: {video_id}, {e}")
            return False
    
    def get_view_count_queue(self) -> Set[int]:
        """获取观看次数队列"""
        if self.redis_client is None:
            logger.warning("Redis不可用，无法获取观看次数队列")
            return set()
        try:
            queue_key = "video:view_count:queue"
            result = self.redis_client.smembers(queue_key)
            return {int(video_id) for video_id in result}
        except Exception as e:
            logger.error(f"获取观看次数队列失败: {e}")
            return set()
    
    def remove_view_count_queue(self, video_ids: List[int]) -> bool:
        """移除观看次数队列中的元素"""
        if self.redis_client is None:
            logger.warning("Redis不可用，无法移除观看次数队列元素")
            return False
        try:
            if not video_ids:
                return True
            queue_key = "video:view_count:queue"
            return bool(self.redis_client.srem(queue_key, *video_ids))
        except Exception as e:
            logger.error(f"移除观看次数队列失败: {e}")
            return False
    
    # 缓存预热相关
    def init_video_zset(self, key: str, videos: List[dict], score_field: str) -> bool:
        """初始化视频ZSET"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法初始化视频ZSET: {key}")
            return False
        try:
            # 先删除旧的ZSET
            self.delete_cache(key)
            # 添加新的元素
            for video in videos:
                self.zadd(key, video.get(score_field, 0), video)
            # 设置过期时间（1小时）
            self.redis_client.expire(key, 3600)
            return True
        except Exception as e:
            logger.error(f"初始化视频ZSET失败: {key}, {e}")
            return False
    
    # 布隆过滤器简单实现
    def add_bloom_filter(self, key: str, value: str) -> bool:
        """添加到布隆过滤器"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法添加布隆过滤器元素: {key}, {value}")
            return False
        try:
            # 使用简单的布隆过滤器实现
            # 这里使用多个哈希函数
            hashes = self._get_bloom_filter_hashes(value)
            pipe = self.redis_client.pipeline()
            for hash_val in hashes:
                pipe.setbit(key, hash_val, 1)
            pipe.execute()
            # 设置过期时间（7天）
            self.redis_client.expire(key, 604800)
            return True
        except Exception as e:
            logger.error(f"添加布隆过滤器失败: {key}, {e}")
            return False
    
    def check_bloom_filter(self, key: str, value: str) -> bool:
        """检查布隆过滤器"""
        if self.redis_client is None:
            logger.warning(f"Redis不可用，无法检查布隆过滤器元素: {key}, {value}")
            return False
        try:
            hashes = self._get_bloom_filter_hashes(value)
            pipe = self.redis_client.pipeline()
            for hash_val in hashes:
                pipe.getbit(key, hash_val)
            results = pipe.execute()
            # 如果所有位都为1，则可能存在
            return all(results)
        except Exception as e:
            logger.error(f"检查布隆过滤器失败: {key}, {e}")
            return False
    
    def _get_bloom_filter_hashes(self, value: str) -> List[int]:
        """获取布隆过滤器的多个哈希值"""
        # 使用简单的哈希函数生成多个哈希值
        hashes = []
        # Jenkins哈希
        hashes.append(self._jenkins_hash(value))
        # DJB2哈希
        hashes.append(self._djb2_hash(value))
        # SDBM哈希
        hashes.append(self._sdbm_hash(value))
        return hashes
    
    def _jenkins_hash(self, key: str) -> int:
        hash_val = 0
        for char in key:
            hash_val += ord(char)
            hash_val += (hash_val << 10)
            hash_val ^= (hash_val >> 6)
        hash_val += (hash_val << 3)
        hash_val ^= (hash_val >> 11)
        hash_val += (hash_val << 15)
        return abs(hash_val)
    
    def _djb2_hash(self, key: str) -> int:
        hash_val = 5381
        for char in key:
            hash_val = ((hash_val << 5) + hash_val) + ord(char)
        return abs(hash_val)
    
    def _sdbm_hash(self, key: str) -> int:
        hash_val = 0
        for char in key:
            hash_val = ord(char) + (hash_val << 6) + (hash_val << 16) - hash_val
        return abs(hash_val)


# 创建全局Redis工具实例（延迟初始化）
_redis_util_instance = None

def get_redis_util() -> RedisUtil:
    """获取Redis工具实例（单例模式）"""
    global _redis_util_instance
    if _redis_util_instance is None:
        _redis_util_instance = RedisUtil()
    return _redis_util_instance

# 兼容旧的导入方式
redis_util = get_redis_util()
